package com.yhb.channel.impl.YPL.http;

import com.yhb.channel.impl.YPL.YPLChannel;
import com.yhb.channel.impl.YPL.YPLConfig;
import com.yhb.channel.impl.YPL.YPLConstant;
import com.yhb.channel.impl.YPL.YPLException;
import com.yhb.channel.provider.IUtilProvider;
import com.yhb.channel.utils.ChannelJSONUtil;
import com.yhb.channel.utils.ChannelLogger;
import com.yhb.channel.utils.ConfigGetter;
import com.yhb.channel.utils.encryptAndSign.ChannelPrivateKeyUtils;
import com.yhb.channel.utils.encryptAndSign.YPLRSAUtils;
import okhttp3.*;
import org.apache.http.util.TextUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.UUID;

@Service
public class YPLHTTPClient {
    @Autowired
    private YPLConfig config;
    @Autowired
    private IUtilProvider iUtilProvider;

    private static ChannelLogger logger;
    private static final YPLRSAUtils yplRSAUtils = new YPLRSAUtils();
    public static String privateKeyStr = null;
    private static final OkHttpClient okHttpClient = new OkHttpClient();
    public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
    public static final String SIGN_HEADER_KEY = "x-efps-sign";
    public static final String SIGN_NO_HEADER_KEY = "x-efps-sign-no";

    @PostConstruct
    private void init() {
        try {
            privateKeyStr = new ChannelPrivateKeyUtils(
                    config.getPrivateKeyFile(), config.getPrivateKeyPassword()).getPrivateKeyString();
        } catch (UnrecoverableKeyException | NoSuchAlgorithmException |
                KeyStoreException | IOException | CertificateException e) {
            throw new YPLException(YPLConstant.ENCRYPT_OR_SIGN_ERROR, "HTTP客户端初始化出错", e);
        }
        logger = new ChannelLogger(YPLChannel.CHANNEL_NAME, YPLException.class, iUtilProvider);
    }

    public String http(String methodName, YPLRequest requestBody, String url) {
        final String jsonData = ChannelJSONUtil.toJson(requestBody);
        final String sign = yplRSAUtils.sign(privateKeyStr, jsonData);
        logger.info("易票联签名结果: " + sign);
        final String requestId = UUID.randomUUID().toString();
        logger.logRequest(methodName, requestId, jsonData);

        try {
            final Request request = new Request.Builder()
                    .url(url)
                    .header(SIGN_HEADER_KEY, sign)
                    .header(SIGN_NO_HEADER_KEY, config.getSignNo())
                    .post(RequestBody.create(jsonData, JSON))
                    .build();
            final Response response = okHttpClient.newCall(request).execute();
            // 检查请求是否成功
            if (!response.isSuccessful()) {
                throw new YPLException(YPLConstant.NETWORK_ERROR, methodName + "响应" + requestId + "网络异常: " + response.code());
            }
            // 取出json
            String resStr = "";
            ResponseBody resBody = response.body();
            if (resBody != null) {
                resStr = resBody.string();
            }
            // 验签
            String resSign = response.header(SIGN_HEADER_KEY);
            String resSignNo = response.header(SIGN_NO_HEADER_KEY);
            if (resSign == null || resSignNo == null || TextUtils.isEmpty(resSign) || TextUtils.isEmpty(resSignNo)) {
                throw new YPLException(YPLConstant.ENCRYPT_OR_SIGN_ERROR, methodName + "响应" + requestId + "签名或响应证书号为空");
            }
            logger.info("响应签名: " + resSign);
            if (!yplRSAUtils.verify(YPLRSAUtils.getKeyFromKeyPath(config.getPublicKeyPath()), resStr, resSign)) {
                throw new YPLException(YPLConstant.ENCRYPT_OR_SIGN_ERROR, methodName + "响应" + requestId + "验签不通过");
            }
            // 返回
            logger.logResponse(methodName, requestId, resStr);
            return resStr;
        } catch (IOException e) {
            throw new YPLException(YPLConstant.ERROR_DEFAULT_CODE, methodName +  "响应" + requestId + "转化为字符串时失败", e);
        }
    }
}
